#include <iostream>
#include <fstream>
#include <vector>
#include <string.h>
#include <sstream>

#include <unistd.h>

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

#include "hashminEnRam.cpp"
//#include "buscarCandidatos.cpp"

#define CANDIDATOS_ESPERADOS 4
#define largo_palabras 6

using namespace std;

vector<Palabra> levantarVectorPalabras(int doc, vector<string> archivos) {
	Palabra palabraAux;
	vector<Palabra> documento;
	std::ifstream ine(archivos[doc].data(),std::ios_base::binary);
	if(ine.good()){	
		while (!ine.eof()) {
			ine.read(palabraAux.palabra, sizeof(char[6])); 		//std::cout << "Reading char[6]: " << std::fixed << palabraAux.palabra << std::endl;
			ine.read((char*)&(palabraAux.frec), sizeof(float)); //std::cout << "Reading floating point number: " << std::fixed << palabraAux.frec << std::endl;			
			documento.push_back(palabraAux);			
		}
		ine.close();
	}
	else {cout << archivos[doc].data()<<endl; cout << "NO PUDO ABRIR EL DOCUMENTO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";}
	return documento;
}

class Cluster {
	public:
	vector<int> docs;
	vector<Palabra> centroide;
	vector<string> direcciones;
	Cluster (vector<string> direcc) {
		direcciones = direcc;
		vector<Palabra> centroide;
		vector<int> docs;
	}
	
	void actualizarCentroide(vector<Palabra> documento){	//cout << "documento.size(): "<<documento.size() << endl; cout <<"centroide.size(): " <<centroide.size() << endl;
		vector<Palabra> centroideActualizado;
		Palabra palabraAux;
		unsigned int contCentro=0, contDoc=0;
		while(contCentro < centroide.size() || contDoc < documento.size()){ //cout << centroideActualizado.size() << endl;
			if (contDoc == documento.size()) {
				while (contCentro < centroide.size()) {
					strcpy(palabraAux.palabra, centroide[contCentro].palabra);			
					palabraAux.frec = (centroide[contCentro].frec)/2;				
					contCentro++;				
					centroideActualizado.push_back(palabraAux);
				}				
			}else if (contCentro == centroide.size()) {
				while (contDoc < documento.size()) {
					strcpy(palabraAux.palabra, documento[contDoc].palabra);
					palabraAux.frec = (documento[contDoc].frec)/2;
					contDoc++;
					centroideActualizado.push_back(palabraAux);
				}
			} else {
			int comparacion = strcmp(centroide[contCentro].palabra, documento[contDoc].palabra);
			if (comparacion == 0){
				strcpy(palabraAux.palabra, centroide[contCentro].palabra);			
				palabraAux.frec = (centroide[contCentro].frec + documento[contDoc].frec)/2;			
				contCentro++;			
				contDoc++;			
				centroideActualizado.push_back(palabraAux);		
			}else if (comparacion < 0){				
				strcpy(palabraAux.palabra, centroide[contCentro].palabra);			
				palabraAux.frec = (centroide[contCentro].frec)/2;				
				contCentro++;				
				centroideActualizado.push_back(palabraAux);
			}else {				
				strcpy(palabraAux.palabra, documento[contDoc].palabra);
				palabraAux.frec = (documento[contDoc].frec)/2;
				contDoc++;
				centroideActualizado.push_back(palabraAux);
				}
			}
		}
		
		centroide = centroideActualizado; //cout <<"centroide.size(): " <<centroide.size() << endl;
	}
	void agregarDoc(int doc) {
		docs.push_back(doc);
		vector<Palabra> docc = levantarVectorPalabras(doc, direcciones);
		actualizarCentroide(docc);
	}
};

class Candidatos {
	public:
		int doc1;
		int doc2;
};

float calcularDistancia(vector<Palabra> documento1, vector<Palabra> documento2, vector<string> archivos);

int clusterMasCercano (vector<Palabra> docNuevo, vector<string> centroides) {
	int distancia, min = MAXIMO, clusterMin = -1;
	vector<Palabra> aux;
	cout << "docNuevo.size(): "<< docNuevo.size()<< endl;
	for (unsigned int i = 0; i < centroides.size(); i++) {
		aux = levantarVectorPalabras(i, centroides); cout << "aux.size(): "<< aux.size()<< endl;
		distancia = calcularDistancia(docNuevo, aux, centroides);	cout << "distancia: "<< distancia<< endl;
		if (distancia < min) { min = distancia; clusterMin = i;}
	}

	return clusterMin;
}

int agregarNuevoArchivo (char* nombreArchivo) {
	vector<int> candidatos;

	vector<string> stop_words = leerStopWords("stop-words-corto.txt");
	vector<Palabra> doc = parsearDocumento(nombreArchivo, stop_words);
	if (!doc.size()) return 0;
	
	struct dirent **namelist;
    int n = 0;
	n = scandir("/tmp/clusters/centroides", &namelist, NULL, alphasort); cout << n<<endl;
	vector<string> archivosCentroides;
	for (int j=0;j < n-2; j++) {
		ostringstream direccion("");
		direccion << "/tmp/clusters/centroides/centroide_" << j;
		archivosCentroides.push_back(direccion.str());
	}cout << "archivosCentroides: " << archivosCentroides.size() <<endl;
	int clusterMin = clusterMasCercano(doc, archivosCentroides);
	// escribirDocumentoDisco(doc, vector de paths .size()
	// agregar la columna a la matriz de hashmin
	// poner el nombre de este arhivo en el archivo del cluster correspondiente
	// poner el nombre de este arhivo en el nombre de todos los archivos.
	

	return clusterMin;
}

int comprobarDirectorio(char* dir){
    struct dirent **namelist;
    int n = 0;
	n = scandir(dir, &namelist, NULL, alphasort); cout << n<<endl;
    if (n < 0) return 0;
	return 1;
}

void listarDocyGrupo() {}
void listarGrupoyDocs() {}

int leerParametros(int argc, char **argv){
	int clusterMin = -1;
	char d[] = "-d", l[] = "-l", g[] = "-g", a[] = "-a";
	if (argc == 1) return 1;
	if (!strcmp(argv[1],d)) {
		if (!comprobarDirectorio(argv[2])) return 2;  //trata de abrir el directorio y ver que tenga archivos dentro
	}
	if (!strcmp(argv[1],l)) {listarDocyGrupo(); return 10;}
	if (!strcmp(argv[1],g)) {listarGrupoyDocs(); return 10;}
	if (!strcmp(argv[1],a)) {
		clusterMin = agregarNuevoArchivo(argv[2]);	cout << "agregado al Cluster: "<<clusterMin<< ".\n";
		if (clusterMin == -1) return 3;
		else cout << "agregado al Cluster: "<<clusterMin<< ".\n";
		return 10;
	}
	return 0;
} 

bool leerBooleano (char* booleano) {
	char Y[] = "Y";
	if (!strcmp(booleano, Y)) return true;
	return false;
}
	
vector<string> abrirDirectorio(char* path) {
	
	vector<string> archivos;
	string dirn(path);
	dirn.append("/");
	DIR *dir = NULL;
	struct dirent *drnt = NULL;
	char punto = '.';
	dir=opendir(dirn.data());
	drnt=readdir(dir);
	int i = 0;
	//printf("%-20s\t", drnt->d_name);
	if(dir)	{
		//printf("output:\n");
		while (drnt) {
			if (drnt->d_name[0] != punto) {
				archivos.push_back(dirn.append(drnt->d_name));
				//cout << dirn <<endl;
				i++;
				dirn.erase(dirn.size()-strlen(drnt->d_name), strlen(drnt->d_name));
				}
			drnt=readdir(dir); 
			//cout << i << endl;
			}
		closedir(dir);
	} else cout<< "Can not open directory: "<< dirn<<endl;
	//cout << i << endl;
	return archivos;
}

void imprimirMatrizHM (int** matrizHM, int cantDocs) {
	for (int u = 0; u<FUNCIONES_HASH; u++) {
		for (int w = 0; w < cantDocs; w++) {
			cout << matrizHM[u][w] << "\t";
		}
		cout << endl;
	}
}

void imprimirClusters (vector<Cluster>& clusters) {
	cout << "clusters.size(): " << clusters.size()<<endl;
	unsigned int doc;
	for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
		cout << "Cluster "<<cluster<<": ";
		for (doc = 0; doc < clusters[cluster].docs.size(); doc++) {
			cout << clusters[cluster].docs[doc]<< "-";
		}
		cout << endl;
	}
}

void eliminarRepetidos(vector<int>& vector) {
	for (unsigned int i=0; i<vector.size()-1; i++) {
		for (unsigned int j=i+1; j<vector.size(); j++) {
			if (vector[i] == vector[j]) vector.erase(vector.begin()+j);
		}
	}
}

float calcularDistancia(vector<Palabra> documento1, vector<Palabra> documento2, vector<string> archivos){      
        unsigned int contDoc1 = 0, contDoc2 = 0;        
        float distanciaTotal = 0.0;
        while (contDoc1 < documento1.size() || contDoc2 < documento2.size()) {          
                if (contDoc1 == documento1.size()){            
                        while(contDoc2 < documento2.size()){                    
                                distanciaTotal += (documento2[contDoc2].frec * documento2[contDoc2].frec);                              
                                contDoc2++;                    
                        }              
                } else {
                        if (contDoc2 == documento2.size()){                    
                                while(contDoc1 < documento1.size()){                            
                                        distanciaTotal += (documento1[contDoc1].frec * documento1[contDoc1].frec);                                      
                                        contDoc1++;                            
                                }                      
                        } else {                                
                                int comparacion = strcmp(documento1[contDoc1].palabra, documento2[contDoc2].palabra);                          
                                if ( comparacion == 0 ){                                
                                        distanciaTotal+= pow((documento1[contDoc1].frec - documento2[contDoc2].frec), 2);                                      
                                        contDoc1++;
                                        contDoc2++;
                                } else if (comparacion < 0){                    
                                        distanciaTotal += (documento1[contDoc1].frec * documento1[contDoc1].frec);                                              
                                        contDoc1++;                                            
                                }else {
                                        distanciaTotal += (documento2[contDoc2].frec * documento2[contDoc2].frec);
                                        contDoc2++;
                                        }
                                }
                        }
                }
        distanciaTotal = sqrt(distanciaTotal);
        return distanciaTotal;
}

int tomarDistancia (int doc, vector<int> maximos, vector<Cluster>& clusters, vector<int>& docsYaClasificados, vector<string> archivos) {
	float min= MAXIMO, aux;		cout << "entro a tomar dist\n";
	bool agregado;
	int docMin= -1;
	vector<vector<Palabra>> vectores;
	vector<Palabra> docAMedir;
	for (unsigned int maximo = 0; maximo < maximos.size(); maximo++) {
		agregado = false;
		for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
			if (maximos[maximo] == clusters[cluster].docs[0]) {
				vectores.push_back(clusters[cluster].centroide); agregado = true;}
			if (doc == clusters[cluster].docs[0]) docAMedir = clusters[cluster].centroide;
		}
		if (!agregado) {
			if (docsYaClasificados[maximos[maximo]] != -1) vectores.push_back(clusters[docsYaClasificados[maximos[maximo]]].centroide);
			else {
				vector<Palabra> doc = levantarVectorPalabras(maximos[maximo], archivos);
				vectores.push_back(doc);
			}
		}
	}
	if (!docAMedir.size()) {
		if (docsYaClasificados[doc]!=-1) docAMedir = clusters[docsYaClasificados[doc]].centroide;
		else {
			cout << "levanto vector: "<< doc <<endl;
			vector<Palabra> docc = levantarVectorPalabras(doc, archivos);
			docAMedir = docc;
			}
	}
	cout << "docAMedir.size(): "<<docAMedir.size()<<endl;
	for (unsigned int vector = 0; vector < vectores.size(); vector++) {
		cout << "vectores[vector].size(): "<<vectores[vector].size()<<"--";
		aux = calcularDistancia(docAMedir, vectores[vector], archivos);
		cout << "dist: " << aux<<" "<<endl;
		if (aux < min) { min = aux; docMin = maximos[vector];}
	}
	cout <<endl;
	return docMin;
}

int obtenerElMasCercano(int doc, vector<int> candidatosConLosQueEsta, int** matrizHM, vector<Cluster>& clusters, int modo, vector<int>& docsYaClasificados, vector<string> archivos){ 
	vector<int> coincidencias; coincidencias.insert(coincidencias.begin(), candidatosConLosQueEsta.size(), 0);
	unsigned int cand, cluster = 0, docADevolver = 0;
	for (int func = 0; func < FUNCIONES_HASH; func++) {
		for (cand = 0; cand < candidatosConLosQueEsta.size(); cand++) {
			if (matrizHM[func][doc] == matrizHM[func][candidatosConLosQueEsta[cand]]) coincidencias[cand]++;
		}
	}
	int max = -1, posMax = -1;
	vector<int> maximos;
	for (cand = 0; cand < coincidencias.size(); cand++){
		if (coincidencias[cand] > max) {
			max = coincidencias[cand];
			posMax = cand;
			maximos.clear();
			maximos.push_back(candidatosConLosQueEsta[cand]);
		} else if (coincidencias[cand] == max) maximos.push_back(candidatosConLosQueEsta[cand]);
	}
	cout << "maximos.size(): "<< maximos.size()<<endl;
	cout << "candidatosConLosQueEsta: "<< candidatosConLosQueEsta[0]<<" "<<candidatosConLosQueEsta[1]<<endl;
	if (maximos.size() == 1) {
		if (modo) return candidatosConLosQueEsta[posMax];
		else {
			for (cluster = 0; cluster < clusters.size(); cluster++) {
				if (clusters[cluster].docs[0] == candidatosConLosQueEsta[posMax]) return cluster;
			}
		}
	} else docADevolver = tomarDistancia(doc, maximos, clusters, docsYaClasificados, archivos);
	if (modo) return docADevolver; // se devuelve el numero de doc
	else {			// se devuelve el numero de cabeza de cluster
		for (cluster = 0; cluster < clusters.size(); cluster++) {
			for (unsigned int docc = 0; docc < clusters[cluster].docs.size(); docc++) {
				if (clusters[cluster].docs[docc] == (int)docADevolver) return cluster;
			}
		}
	}
	return -1;
}

void unirClusters(int cluster1, int cluster2, vector<Candidatos>& candidat, int numCand, vector<Cluster>& clusters, vector<int>& docsYaClasificados) {
	unsigned int i, j;
	if (clusters[cluster1].docs[0] < clusters[cluster2].docs[0]) {
		for (i = numCand+1; i < candidat.size(); i++) {
			if (candidat[i].doc1 == clusters[cluster2].docs[0]) candidat[i].doc1 = clusters[cluster1].docs[0];
			else if (candidat[i].doc2 == clusters[cluster2].docs[0]) candidat[i].doc2 = clusters[cluster1].docs[0];
		}
		for (j = 0; j < docsYaClasificados.size(); j++) {
			if (docsYaClasificados[j] == cluster2) docsYaClasificados[j] = cluster1;
			else if (docsYaClasificados[j] > cluster2) docsYaClasificados[j]--;
			}
		clusters[cluster1].docs.insert(clusters[cluster1].docs.end(), clusters[cluster2].docs.begin(), clusters[cluster2].docs.end());
		clusters.erase(clusters.begin()+cluster2);
	} else {
		for (i = numCand+1; i < candidat.size(); i++) {
			if (candidat[i].doc1 == clusters[cluster1].docs[0]) candidat[i].doc1 = clusters[cluster2].docs[0];
			else if (candidat[i].doc2 == clusters[cluster1].docs[0]) candidat[i].doc2 = clusters[cluster2].docs[0];
		}
		for (j = 0; j < docsYaClasificados.size(); j++) {
			if (docsYaClasificados[j] == cluster1) docsYaClasificados[j] = cluster2;
			else if (docsYaClasificados[j] > cluster1) docsYaClasificados[j]--;
			}
		clusters[cluster2].docs.insert(clusters[cluster2].docs.end(), clusters[cluster1].docs.begin(), clusters[cluster1].docs.end());
		clusters.erase(clusters.begin()+cluster1);
	}
}

void eliminarRepetDeCandidatos (vector<Candidatos>& candidTemp) {
	unsigned int j;
	if (!candidTemp.size()) return;
	for (unsigned int i = 0; i < candidTemp.size()-1; i++) {
		for (j=i+1; j < candidTemp.size(); j++) {
			if (candidTemp[i].doc1 == candidTemp[j].doc1 && candidTemp[i].doc2 == candidTemp[j].doc2) candidTemp.erase(candidTemp.begin()+j);
			else if (candidTemp[i].doc1 == candidTemp[j].doc2 && candidTemp[i].doc2 == candidTemp[j].doc1) candidTemp.erase(candidTemp.begin()+j);
		}
	}
}

void analizarDondeVa(int doc, int** matrizHM ,vector<Candidatos>& candidat, vector<Cluster>& clusters, int numCand, vector<int>& docsYaClasificados, vector<string> archivos, int numeroDeClustersIngresado) {
	cout << "doc a analizar: "<<doc<<endl;
	vector<int> candidatosConLosQueEsta;
	int cabeza = -1, numCluster, ver;
	bool esCabeza = false;
	unsigned int cand;
	if (candidat[numCand].doc1 == doc) ver = candidat[numCand].doc2;
	else ver = candidat[numCand].doc1;
	for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
		if (doc == clusters[cluster].docs[0]) {cabeza = doc; numCluster = cluster;}
		if (ver == clusters[cluster].docs[0]) esCabeza = true;
	}
	if (cabeza != -1 && !esCabeza) return;
	else if (cabeza != -1 && esCabeza) {
		for (cand = numCand; cand < candidat.size(); cand++) {
			for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
				if ((candidat[cand].doc1 == doc) && (candidat[cand].doc2 == clusters[cluster].docs[0]) && (clusters[cluster].docs[0]!= doc)) candidatosConLosQueEsta.push_back(candidat[cand].doc2);
				else if ((candidat[cand].doc2 == doc) && (candidat[cand].doc1 == clusters[cluster].docs[0])&& (clusters[cluster].docs[0]!= doc)) candidatosConLosQueEsta.push_back(candidat[cand].doc1);
			}
		}
	} else if (cabeza==-1 && docsYaClasificados[doc] != -1) return;
	else {
		for (cand = numCand; cand < candidat.size(); cand++) {
			//for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
				if ((candidat[cand].doc1 == doc)) candidatosConLosQueEsta.push_back(candidat[cand].doc2);
				else if ((candidat[cand].doc2 == doc)) candidatosConLosQueEsta.push_back(candidat[cand].doc1);
			//}
		}
	}
	if (!candidatosConLosQueEsta.size() && (cabeza!=-1)) return;
		//unirClusters(numCluster, docsYaClasificados[candidat[numCand].doc1], candidat, numCand, clusters, docsYaClasificados);
	else if (candidatosConLosQueEsta.size() == 1 && (cabeza==-1)) {cout << docsYaClasificados[candidatosConLosQueEsta[0]] << " " <<candidatosConLosQueEsta[0]<<endl; 
		if (docsYaClasificados[candidatosConLosQueEsta[0]] != -1) {	clusters[docsYaClasificados[candidatosConLosQueEsta[0]]].agregarDoc(doc); docsYaClasificados[doc] = docsYaClasificados[candidatosConLosQueEsta[0]];}
		else {
			Cluster nuevoCluster(archivos);
			nuevoCluster.agregarDoc(candidatosConLosQueEsta[0]); nuevoCluster.agregarDoc(doc);
			clusters.push_back(nuevoCluster);
			docsYaClasificados[candidatosConLosQueEsta[0]] = clusters.size()-1;
			docsYaClasificados[doc] = clusters.size()-1;
		}
	} else if (candidatosConLosQueEsta.size() == 1 && (cabeza!=-1)) {		
		if (numeroDeClustersIngresado && (int)clusters.size() > numeroDeClustersIngresado) {unirClusters(numCluster, docsYaClasificados[candidatosConLosQueEsta[0]], candidat, numCand, clusters, docsYaClasificados); eliminarRepetDeCandidatos(candidat);}
		else if (!numeroDeClustersIngresado) {unirClusters(numCluster, docsYaClasificados[candidatosConLosQueEsta[0]], candidat, numCand, clusters, docsYaClasificados); eliminarRepetDeCandidatos(candidat);}
	} else { cout << "candidatosConLosQueEsta.size(): "<<candidatosConLosQueEsta.size() <<" "<<candidatosConLosQueEsta[0]<<candidatosConLosQueEsta[1]<< endl;
		int clusterMasCercano, docMasCercano;
		if (cabeza!=-1) {
			clusterMasCercano = obtenerElMasCercano(doc, candidatosConLosQueEsta, matrizHM, clusters, 0, docsYaClasificados, archivos);
			cout << "clusterMasCercano: " << clusterMasCercano << endl;
			if (numeroDeClustersIngresado && (int)clusters.size() > numeroDeClustersIngresado) {unirClusters(numCluster, clusterMasCercano, candidat, numCand, clusters, docsYaClasificados); eliminarRepetDeCandidatos(candidat);}//en este caso es un cluster
			else if (!numeroDeClustersIngresado) {unirClusters(numCluster, clusterMasCercano, candidat, numCand, clusters, docsYaClasificados); eliminarRepetDeCandidatos(candidat);}
		} else {
			numCluster = -1;
			docMasCercano = obtenerElMasCercano(doc, candidatosConLosQueEsta, matrizHM, clusters, 1, docsYaClasificados,  archivos);
			cout << "docMasCercano: " << docMasCercano << endl;
			for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
				if (docMasCercano == clusters[cluster].docs[0]) {numCluster = cluster;}
			}
			if (numCluster > -1) {clusters[numCluster].agregarDoc(doc); docsYaClasificados[doc] = numCluster;}
			else {
				if (docsYaClasificados[docMasCercano] != -1) {clusters[docsYaClasificados[docMasCercano]].agregarDoc(doc); docsYaClasificados[doc] = docsYaClasificados[docMasCercano];}
				else {
					Cluster nuevoCluster(archivos);
					nuevoCluster.agregarDoc(docMasCercano); nuevoCluster.agregarDoc(doc);
					clusters.push_back(nuevoCluster);
					docsYaClasificados[docMasCercano] = clusters.size()-1; docsYaClasificados[doc] = clusters.size()-1;
				}
			}
		}
	}
}

void agregarACluster(vector<Candidatos>& candidat,int** matrizHM, vector<Cluster>& clusters, vector<int>& docsYaClasificados, int size, bool repet, vector<string> archivos, int numeroDeClustersIngresado) { //cout << "Entro ";
	int estaElPrimero, estaElSegundo, cabeza1, cabeza2, cluster1, cluster2;
	if (!clusters.size() && candidat.size()) {
		Cluster nuevoCluster(archivos);
		nuevoCluster.agregarDoc(candidat[0].doc1); nuevoCluster.agregarDoc(candidat[0].doc2);
		clusters.push_back(nuevoCluster);
		docsYaClasificados[candidat[0].doc1] = clusters.size()-1; docsYaClasificados[candidat[0].doc2] = clusters.size()-1;
		for (unsigned int cand = 1; cand < candidat.size(); cand++) {
			for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
				estaElPrimero = 0; estaElSegundo = 0;
				for (unsigned int doc = 0; doc < clusters[cluster].docs.size(); doc++) {
					if (clusters[cluster].docs[doc] == candidat[cand].doc1 ) estaElPrimero = true;
					if (clusters[cluster].docs[doc] == candidat[cand].doc2 ) estaElSegundo = true;
				}
				if (estaElPrimero && !estaElSegundo) {clusters[cluster].agregarDoc(candidat[cand].doc2); docsYaClasificados[candidat[cand].doc2] = cluster;}
				else if (!estaElPrimero && estaElSegundo) {clusters[cluster].agregarDoc(candidat[cand].doc1); docsYaClasificados[candidat[cand].doc1] = cluster;}
				else if (!estaElPrimero && !estaElSegundo) {
					Cluster nuevoCluster(archivos);
					nuevoCluster.agregarDoc(candidat[cand].doc1); nuevoCluster.agregarDoc(candidat[cand].doc2);
					clusters.push_back(nuevoCluster);
					docsYaClasificados[candidat[cand].doc1] = clusters.size()-1; docsYaClasificados[candidat[cand].doc2] = clusters.size()-1;
				}
			}
		}return;
	}
	for (unsigned int candi = 0; candi < candidat.size(); candi++) { //imprimirClusters(clusters);
	cout << "leyendo el prox cand: "<<candidat[candi].doc1<<"-"<<candidat[candi].doc2<< "\n";
		if (!repet) { 
			
			cout << docsYaClasificados[candidat[candi].doc1] << " " << docsYaClasificados[candidat[candi].doc2]<< endl;
			if (candidat[candi].doc1 != candidat[candi].doc2) {
				if (docsYaClasificados[candidat[candi].doc1] != -1 && docsYaClasificados[candidat[candi].doc2] != -1 ) {
					if (docsYaClasificados[candidat[candi].doc1] != docsYaClasificados[candidat[candi].doc2]) {
						analizarDondeVa(candidat[candi].doc1, matrizHM ,candidat, clusters, candi, docsYaClasificados, archivos, numeroDeClustersIngresado);
						if (docsYaClasificados[candidat[candi].doc1] != docsYaClasificados[candidat[candi].doc2]) analizarDondeVa(candidat[candi].doc2, matrizHM ,candidat, clusters, candi, docsYaClasificados, archivos, numeroDeClustersIngresado);
					}
				} else {
					if (docsYaClasificados[candidat[candi].doc1] == -1) analizarDondeVa(candidat[candi].doc1,matrizHM ,candidat, clusters, candi, docsYaClasificados, archivos, numeroDeClustersIngresado);
					if (docsYaClasificados[candidat[candi].doc2] == -1) analizarDondeVa(candidat[candi].doc2, matrizHM,candidat, clusters, candi, docsYaClasificados, archivos, numeroDeClustersIngresado);
				}//docsYaClasificados[candidat[cand].doc1] = 1; docsYaClasificados[candidat[cand].doc2] = 1;
			}
		} else {
			cabeza1 = 0; cabeza2 = 0;
			for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
				if (candidat[candi].doc1 == clusters[cluster].docs[0]) {cabeza1 = candidat[candi].doc1; cluster1 = cluster;}
				if (candidat[candi].doc2 == clusters[cluster].docs[0]) {cabeza2 = candidat[candi].doc2; cluster2 = cluster;}
			}
			if (cabeza1 && cabeza2) {
				if (cabeza1 != cabeza2) {
					if (numeroDeClustersIngresado && (int)clusters.size() > numeroDeClustersIngresado) {unirClusters(cluster1, cluster2, candidat, candi, clusters, docsYaClasificados); eliminarRepetDeCandidatos(candidat);}
					else if (!numeroDeClustersIngresado) {unirClusters(cluster1, cluster2, candidat, candi, clusters, docsYaClasificados); eliminarRepetDeCandidatos(candidat);}
				}
			} else if (!cabeza1 && !cabeza2) {
				Cluster nuevoCluster(archivos);
				nuevoCluster.agregarDoc(candidat[candi].doc1); nuevoCluster.agregarDoc(candidat[candi].doc2);
				docsYaClasificados[candidat[candi].doc1] = 0; docsYaClasificados[candidat[candi].doc2] = 0;
				clusters.push_back(nuevoCluster);
			}else {
				if (!cabeza1) {clusters[cluster2].agregarDoc(candidat[candi].doc1); docsYaClasificados[candidat[candi].doc1] = 0;}
				if (!cabeza2) {clusters[cluster1].agregarDoc(candidat[candi].doc2); docsYaClasificados[candidat[candi].doc2] = 0;}
			}		
		}
	}
}

int** leerMHM (int numeroDeDocs, string direc) {
	std::fstream fs (direc.data(), std::fstream::in);
	if (!fs) return NULL;
	fs.seekg(0, fs.end);
	int length = fs.tellg(); //cout << length <<endl;
	fs.seekg(0, fs.beg);
	char buffer[length];
	fs.read(buffer,length); //if (buffer[4] == '\t') cout << buffer[4] <<buffer[5]<<endl;
	fs.close();
	string numero("");
	vector <int> elemMatriz;
	int r =0, j = 0;
	while(r<length) {
		while (buffer[r]!= '\t' && buffer[r]!= '\n') {numero.push_back(buffer[r]); r++;}
		if (numero.size() > 0 ) {
			j++;
			elemMatriz.push_back(atoi(numero.data()));
			numero.clear();
		}
		r++;
	}
	int ** matrizHashMin = (int**) malloc (sizeof(int*)*FUNCIONES_HASH);
	for(int i=0; i< FUNCIONES_HASH; i++) {
		matrizHashMin[i]= (int *)malloc(sizeof(int)*numeroDeDocs);
		for(int j=0; j<numeroDeDocs; j++) { 
			matrizHashMin[i][j]= elemMatriz[(i*numeroDeDocs)+j];
		}
	}
	return matrizHashMin;
}

// se fija la cabeza de cada cluster sea diferente de las demas.
void ordenarClusters (vector<Cluster> clusters) {
	vector<int> cabezasDeClusters;
	unsigned int aux, i, j, doc, cab; cout << "cabezas de cluster pre: ";
	for (unsigned int cluster=0; cluster < clusters.size(); cluster++){
		cabezasDeClusters.push_back(clusters[cluster].docs[0]); cout <<clusters[cluster].docs[0] <<"-";
	}cout<< endl;
	bool encontrado = true;
	for (i = 0; i< cabezasDeClusters.size()-1; i++) {
		for (j = i+1; j< cabezasDeClusters.size(); j++) {
			if (cabezasDeClusters[i] == cabezasDeClusters[j]) {
				for (doc = 1; doc < clusters[i].docs.size(); doc++) {
					encontrado = true;
					for (cab = 0; cab < cabezasDeClusters.size(); cab++) {
						if (clusters[i].docs[doc] == cabezasDeClusters[cab]) encontrado = false;
					}
					if (encontrado) {
						cabezasDeClusters[i] = clusters[i].docs[doc];
						aux = clusters[i].docs[doc];
						clusters[i].docs.erase(clusters[i].docs.begin()+doc);
						clusters[i].docs.insert(clusters[i].docs.begin(), aux);
						break;
					}
				}
				if (!encontrado) {
					for (doc = 1; doc < clusters[j].docs.size(); doc++) {
						encontrado = true;
						for (cab = 0; cab < cabezasDeClusters.size(); cab++) {
							if (clusters[j].docs[doc] == cabezasDeClusters[cab]) encontrado = false;
						}
						if (encontrado) {
							cabezasDeClusters[j] = clusters[j].docs[doc];
							aux = clusters[j].docs[doc];
							clusters[j].docs.erase(clusters[j].docs.begin()+doc);
							clusters[j].docs.insert(clusters[j].docs.begin(), aux);
							break;
						}
					}
				}
			}
		}
	}
	cout << "cabezas de cluster post: ";
	for (unsigned int cluster=0; cluster < clusters.size(); cluster++){
		cabezasDeClusters.push_back(clusters[cluster].docs[0]); cout <<clusters[cluster].docs[0] <<"-";
	}cout<< endl;
}

void ponerCentroidesSinRepet (int** matrizHM, vector<Cluster> clusters, int& docsIgnorados) {
	unsigned int func, doc, min = MAXIMO, cluster;
	//if (clusters.size()) ordenarClusters(clusters);
	for (cluster= 0; cluster < clusters.size(); cluster++) {
		for (func = 0; func < FUNCIONES_HASH; func++) {
			min = MAXIMO;
			for (doc = 0; doc < clusters[cluster].docs.size(); doc++) { 
				if (matrizHM[func][clusters[cluster].docs[doc]] < (int)min) min = matrizHM[func][clusters[cluster].docs[doc]];
			}
			matrizHM[func][clusters[cluster].docs[0]] = min;
		}
		for (doc = 1; doc < clusters[cluster].docs.size(); doc++) {
			for (func = 0; func < FUNCIONES_HASH; func++) {
				matrizHM[func][clusters[cluster].docs[doc]] = (MAXIMO - docsIgnorados);
			}
			docsIgnorados++;
		}
	}
	//if (clusters.size()) imprimirMatrizHM(matrizHM, 48);
}

void ponerCentroidesConRepet (int** matrizHM, vector<Cluster> clusters, int& docsIgnorados) {
	unsigned int func, doc, min = MAXIMO, tester, cluster;
	if (clusters.size()) ordenarClusters(clusters);
	for (cluster= 0; cluster < clusters.size(); cluster++) {
		for (func = 0; func < FUNCIONES_HASH; func++) {
			min = MAXIMO;
			for (doc = 0; doc < clusters[cluster].docs.size(); doc++) {
				if (matrizHM[func][clusters[cluster].docs[doc]] < (int)min) min = matrizHM[func][clusters[cluster].docs[doc]];
			}
			matrizHM[func][clusters[cluster].docs[0]] = min;
		}
	}
	for (cluster= 0; cluster < clusters.size(); cluster++) {
		for (doc = 1; doc < clusters[cluster].docs.size(); doc++) {
				tester = 0;
				for (func = 0; func < FUNCIONES_HASH; func++) {
					if (matrizHM[func][clusters[cluster].docs[doc]] > (MAXIMO-docsIgnorados-1)) {tester = 1; break;}
					matrizHM[func][clusters[cluster].docs[doc]] = (MAXIMO - docsIgnorados);
				}
				if (!tester) docsIgnorados++;
			}
		}
}

void redimensionarClusters(vector<Cluster>& clusters, int numeroDeClustersIngresado, int** matrizHM, vector<int> docsYaClasificados) {
	vector<int> cabezas, aux, coincidencias; 
	vector<Candidatos> candidatos;
	int clusterAnalizado, clusterMasCercano, maxCoincidencias = 0;
	unsigned int cand, cluster;
	while ((int)clusters.size() > numeroDeClustersIngresado) {
		for (cluster=0; cluster < clusters.size(); cluster++) cabezas.push_back(clusters[cluster].docs[0]);
		for (cluster=0; cluster < clusters.size(); cluster++) {
			coincidencias.clear();
			coincidencias.insert(coincidencias.begin(), cabezas.size(), 0);
			for (int func = 0; func < FUNCIONES_HASH; func++) {
				for (cand = 0; cand < cabezas.size(); cand++) {
					if (cand!=cluster && (matrizHM[func][cabezas[cluster]] == matrizHM[func][cabezas[cand]])) coincidencias[cand]++;
				}
			}
			int max = -1; //posMax = -1;
			vector<int> maximos;
			for (cand = 0; cand < coincidencias.size(); cand++) {
				if (coincidencias[cand] > max) {
					max = coincidencias[cand];
					//posMax = cand;
					maximos.clear();
					maximos.push_back(cabezas[cand]);
				} else if (coincidencias[cand] == max) maximos.push_back(cabezas[cand]);
			}
			if (max > maxCoincidencias) {maxCoincidencias = max; clusterAnalizado = cluster; clusterMasCercano = maximos[0];}
		}
		for (unsigned int cluster1=0; cluster1 < clusters.size(); cluster1++) if (clusterMasCercano == clusters[cluster1].docs[0]) {clusterMasCercano = cluster1; break;}
		cout << "unir clusters: "<< clusterAnalizado << " " << clusterMasCercano << endl;
		unirClusters(clusterAnalizado, clusterMasCercano, candidatos, 0, clusters, docsYaClasificados);
		// de los clusters hechos se buscan los mas cercanos;
		// luego unirClusters();
	}
}

void copiarMatriz(int** matrizHM, int** matrizHMCopia, int numeroDeDocsTotales) {
	for (int i=0; i<FUNCIONES_HASH; i++) {
		for (int j = 0; j < numeroDeDocsTotales; j++) {
			matrizHMCopia[i][j] = matrizHM[i][j];
		}
	}
}

int** buscarCandidat(int** matrizHM, int numeroDeDocsTotales, vector<Cluster>& clusters,vector<int>& docsYaClasificados, bool repet, int numeroDeClustersIngresado, vector<string> archivos) {
	//vector<int> ubicacionDocs; ubicacionDocs.insert(ubicacionDocs.begin(), 48, -1);
	float tamanios[9] = {1,2,3,4,6,8,12,16,24};
	float tamBloque;
	int** matrizHMcopia = crearMatrizHashMin(numeroDeDocsTotales);
	int numeroDeClustersActual = 0, doc, docAComp, docsIgnorados = 0, comienzo;
	for (int tam = 8; tam > -1; tam--) {
		for (int menos = 0; menos < (tamanios[tam]-tamanios[tam-1]); menos++) {
			comienzo = 0;
			tamBloque = tamanios[tam];
			float coincidencias;
			vector<int> docsYaClasificadosCopia;
			vector<Cluster> clustersCopia;
			vector<Candidatos> candidatosTemp;
			float coincidenciaEnBloque = ((tamBloque-menos)/tamBloque); cout << "coincidenciaEnBloque: "<<(tamBloque-menos)<< "/"<<tamBloque<<endl;
			while (comienzo < FUNCIONES_HASH) {
				for (doc = 0; doc < numeroDeDocsTotales-1; doc++) {
					for (docAComp = (doc+1); docAComp < numeroDeDocsTotales; docAComp++) {
						coincidencias = 0;
						for (int funcHash = comienzo; funcHash < (tamBloque+comienzo); funcHash++) {
							if (matrizHM[funcHash][doc] == matrizHM[funcHash][docAComp]) {coincidencias++;}
							}
						if ((coincidencias/tamBloque) >= coincidenciaEnBloque) {
							cout << " - candidatos = " << doc << "-" <<docAComp;
							Candidatos par; par.doc1 = doc; par.doc2 = docAComp;
							candidatosTemp.push_back(par);				
							}
						} 
					}
					comienzo+=tamBloque;
				}
				copiarMatriz(matrizHM, matrizHMcopia, numeroDeDocsTotales);
				//matrizHMcopia = (void**)memcpy(matrizHMcopia, matrizHM, sizeof(matrizHM)*48);
				docsYaClasificadosCopia = docsYaClasificados;
				clustersCopia = clusters;
				numeroDeClustersActual = clusters.size(); cout << "candidatosTemp.size(): "<<candidatosTemp.size()<<endl;
				eliminarRepetDeCandidatos(candidatosTemp); cout << "candidatosTemp.size(): "<<candidatosTemp.size()<<endl;
//imprimirMatrizHM(matrizHM, 48);	cout << "\n\n";		
cout << "hasta aca llega"<<endl;	
				agregarACluster(candidatosTemp, matrizHM,clusters, docsYaClasificados, numeroDeDocsTotales, repet, archivos, numeroDeClustersIngresado);				
				
				if (clusters.size()) {imprimirClusters(clusters);}
				if (repet) ponerCentroidesConRepet(matrizHM, clusters, docsIgnorados);
				else ponerCentroidesSinRepet(matrizHM, clusters, docsIgnorados);
//if (candidatosTemp.size()) {imprimirMatrizHM(matrizHM, 48); return NULL;}
				cout << "hasta aca ok"<<endl; //imprimirMatrizHM(matrizHM, 48); ///////
				if (numeroDeClustersIngresado) {
					if ((int)clusters.size() == numeroDeClustersIngresado) return matrizHM;
					else if ((int)clusters.size() > numeroDeClustersIngresado) {
						redimensionarClusters(clusters, numeroDeClustersIngresado, matrizHM, docsYaClasificados);
						if (repet) ponerCentroidesConRepet(matrizHM, clusters, docsIgnorados);
						else ponerCentroidesSinRepet(matrizHM, clusters, docsIgnorados);
						return matrizHM;
					}else {
						if ((int)clusters.size() <  numeroDeClustersActual-1) {
							clusters = clustersCopia;
							docsYaClasificados = docsYaClasificadosCopia;
							return matrizHMcopia;
						}
					}						
				} else if ((int)clusters.size() <  numeroDeClustersActual-1) {//imprimirMatrizHM(matrizHM, 48);cout << endl<<endl;imprimirMatrizHM(matrizHMcopia, 48);
					clusters = clustersCopia;
					docsYaClasificados = docsYaClasificadosCopia;
					return matrizHMcopia;
				}
				candidatosTemp.clear(); //sleep(2);
		}		
	}
	return matrizHM;
}

void ubicarDocsSinClasificar (int** matrizHM, vector<Cluster>& clusters, vector<int>& docsYaClasificados, vector<string> archivos) {
	vector<int> cabezasDeClusters;cout << "entro /////////////////////////////////////////"<<clusters.size()<<"\n"; //imprimirMatrizHM(matrizHM,archivos.size());
	int clusterMasCercano;
	for (unsigned int cluster=0; cluster < clusters.size(); cluster++){
		cabezasDeClusters.push_back(clusters[cluster].docs[0]);
	}
	for (unsigned int doc = 0; doc < docsYaClasificados.size(); doc++) {
		if (docsYaClasificados[doc] == -1) {
			cout <<"doc no clasificado = " <<doc << endl;
			clusterMasCercano = obtenerElMasCercano(doc, cabezasDeClusters, matrizHM, clusters, 0, docsYaClasificados, archivos);
			cout <<"clusterMasCercano = " <<clusterMasCercano << endl;
			clusters[clusterMasCercano].agregarDoc(doc); cout << "Se agrego "<<doc<<" al cluster "<<clusterMasCercano<<".\n";
		}
	}
}

void llenarVector (vector<string>& direcc, vector<string> archivos) {
	for (unsigned int i= 0; i <archivos.size(); i++) {
		ostringstream direccion("");
		direccion << "/tmp/docsParseados/Documento_" << i;
		direcc.push_back(direccion.str());
	}
}

string escribirCentroidesEnDisco(vector<Palabra> centroide, int numeroDeCluster){
	
	ostringstream direccion;
	direccion << "/tmp/clusters/centroides/centroide_" << numeroDeCluster;	
	std::ofstream ou(direccion.str(),std::ios_base::binary);
	if(ou.good()) {
		float f3= 0;
		for (unsigned int i=0; i < centroide.size(); i++) {
			f3 = centroide[i].frec;
			ou.write(centroide[i].palabra, sizeof(char[6]));
			ou.write((char*)&f3, sizeof(float));
		}
	}
	ou.close();
	return direccion.str();
}


string escribirDocumentoDisco(vector<Palabra> doc, int numeroDeDoc){
	ostringstream direccion;
	direccion << "/tmp/docsParseados/Documento_" << numeroDeDoc;	
	std::ofstream ou(direccion.str(),std::ios_base::binary);
	if(ou.good()) {
		float f3= 0;
		for (unsigned int i=0; i < doc.size(); i++) {
			f3 = doc[i].frec;
			ou.write(doc[i].palabra, sizeof(char[6]));
			ou.write((char*)&f3, sizeof(float));
		}
	}
	ou.close();
	return direccion.str();
}

void guardarMatriz(string direccionMatriz, int** matrizHM, int numDocsTotales) {
	ostringstream buffer;
	int j ;
	std::ofstream ou(direccionMatriz.data(),std::ofstream::out);
	if(ou.good()) {
		for (int i = 0; i < FUNCIONES_HASH; i++) {
			for (j = 0; j < numDocsTotales; j++) {
				ostringstream buffer("");
				buffer << matrizHM[i][j]<<"\t";
				ou.write(buffer.str().data(), strlen(buffer.str().data()));
			}
			ostringstream buffer("");
			buffer <<endl;
			ou.write(buffer.str().data(), buffer.str().size());
		}
	}
	ou.close();
}

void guardarDocumentosDeCluster (vector<Cluster> clusters, vector<string> archivos) {
	system("mkdir /tmp/clusters/docs");
	string buffer;
	for (unsigned int cluster = 0; cluster < clusters.size(); cluster++) {
		ostringstream direccion;
		direccion << "/tmp/clusters/docs/cluster_" << cluster;	
		std::ofstream ou(direccion.str(),std::ofstream::out);
		if(ou.good()) {
			for (unsigned int doc = 0; doc < clusters[cluster].docs.size(); doc++) {
				buffer = archivos[clusters[cluster].docs[doc]];
				ou.write(buffer.data(), strlen(buffer.data()));
				buffer.clear();
				ou.write("\n", strlen("\n"));
			}
		}
		ou.close();
	}
}

int main(int argc, char** argv) {
	char c[] = "-c", o[] = "-o";
	int numeroDeClustersIngresado = 0;
	bool repet = false;
	int queHacer = leerParametros(argc,argv);
	if (queHacer == 10) return 0;
	if (queHacer == 1 || queHacer == 2 || queHacer == 3) { cout <<"Error de entrada de parametros."<<endl; return 0; }
	if ((argc > 4) && !strcmp(argv[3],c)) numeroDeClustersIngresado = atoi(argv[4]); cout <<"numeroDeClustersIngresado: "<<numeroDeClustersIngresado<<endl;
	if ((argc > 4) && !strcmp(argv[3], o)) repet = leerBooleano(argv[4]); 
	else if ((argc > 6) &&!strcmp(argv[5], o)) repet = leerBooleano(argv[6]);cout <<"repet: "<<repet<<endl;
	
	char* dir= argv[2];
	vector<string> archivos = abrirDirectorio(dir);
	vector<int> docsYaClasificados; docsYaClasificados.insert(docsYaClasificados.begin(), archivos.size(), -1);
	vector<Cluster> clusters;
	int** matrizHM;
	vector<string> archivosParseados;	
	
	struct dirent **namelist;
    int n = 0;
	n = scandir("/tmp/docsParseados", &namelist, NULL, alphasort);
	if (n<0) {
		
		system("mkdir /tmp/docsParseados");
	
		matrizHM = crearMatrizHashMin(archivos.size());
		vector<string> stop_words = leerStopWords("/home/fernando/Escritorio/stop-words-corto.txt");

		for (unsigned int numArch = 0; numArch < archivos.size(); numArch++) { //archivos.size()
				vector<Palabra> doc = parsearDocumento(archivos[numArch].data(), stop_words); // doc es el vector de palabras-frec del doc
				archivosParseados.push_back(escribirDocumentoDisco(doc, numArch));
				modificarMatrizHashMin (matrizHM, numArch, doc);
				
				//guardar VectorArchivosDeDocumentos
		}
		string direc("/home/fernando/Escritorio/matriz");
		guardarMatriz(direc, matrizHM, archivos.size());
		
		
		
	} else {
		string direc("/home/fernando/Escritorio/matriz");
		matrizHM = leerMHM(archivos.size(), direc);
		llenarVector(archivosParseados, archivos); //Levantar vector archivos docs
	}
	matrizHM = buscarCandidat(matrizHM, archivos.size(), clusters, docsYaClasificados, repet, numeroDeClustersIngresado, archivosParseados);
	ubicarDocsSinClasificar(matrizHM, clusters, docsYaClasificados, archivosParseados);
	
	imprimirClusters(clusters);

	for (unsigned int i=0; i < archivos.size();i++) cout << i <<" "<<archivos[i].substr(71)<< "\t";
	cout <<endl;
	system("mkdir /tmp/clusters/"); system("mkdir /tmp/clusters/centroides");
	for (unsigned int cluster= 0; cluster < clusters.size(); cluster++) {
		escribirCentroidesEnDisco(clusters[cluster].centroide, cluster);
	}
	
	guardarDocumentosDeCluster(clusters, archivos);
	
	/*
	int** matrizHM = crearMatrizHashMin(archivos.size());
	//imprimirMatrizHM(matrizHM, archivos.size());

	vector<string> stop_words = leerStopWords("stop-words-corto.txt");

	for (unsigned int numArch = 0; numArch < archivos.size(); numArch++) { //archivos.size()

			vector<Palabra> doc = parsearDocumento(archivos[numArch].data(), stop_words); // doc es el vector de palabras-frec del doc
			//sleep(10);
			archivosParseados.push_back(escribirDocumentoDisco(doc, numArch));
			modificarMatrizHashMin (matrizHM, numArch, doc);
			//imprimirMatrizHM(matrizHM,archivos.size());//archivos.size()
	}
	*/
	//int** matrizHM = leerMHM(archivos.size());
	//guardarMatrizHM(matrizHM);
	 //archivos.size()
	//if ((int)clusters.size() < numeroDeClustersIngresado) {}//ver a que cluster iria cada uno de los que quedan sin clasificar y con estos armar los restantes clusters.
	//imprimirClusters(clusters);

	
//cout <<"repet: "<<repet<<endl;
	//float a = 7, b = 8;
	//cout << (a/b) << endl;
	/*char fer[6] = "ferna";
	cout << fer << endl;
	fer[4] = 0;
	cout << fer << endl; cout << (int)fer[4] << endl;
	cout << (int)'a' << "-" << (int) 'z' << "-"<<(char)0<<endl;*/
	return 0;
}
